/*
 * libc/environ/environ.c
 */

#include <stddef.h>
#include <environ.h>
#include <malloc.h>
#include <string.h>
#include <cJSON.h>
#include <log.h>

static cJSON *__xenviron = NULL;

void __init_env()
{
	__xenviron = cJSON_CreateObject();
}

//TODO: re-alloc data to pointer every time. Is it reasonable?
char **__get_environ(void)
{
	static char **v = NULL;

	if (v)
	{
		for (char **i = v; *i; i++)
		{
			free(*i);
		}
		free(v);
	}

	int count = cJSON_GetArraySize(__xenviron);

	if (count)
	{
		v = malloc(sizeof(char *) * (count + 1));
	}

	if (!count || !v)
	{
		return NULL;
	}

	const cJSON *item;
	int i = 0;
	cJSON_ArrayForEach(item, __xenviron)
	{
		char *name = item->string;
		char *value = cJSON_GetStringValue(item);

		size_t l1, l2;
		l1 = strlen(name);
		l2 = strlen(value);

		char *s = malloc(l1 + l2 + 2);

		memcpy(s, name, l1);
		s[l1] = '=';
		memcpy(s + l1 + 1, value, l2 + 1);

		v[i] = s;
		i++;
	}
	v[i] = NULL;
	return v;
}

int __put_env(char *name, char *value, int overwrite)
{
	if (cJSON_GetObjectItemCaseSensitive(__xenviron, name) != NULL)
	{
		if (!overwrite)
			return 0;
	}

	cJSON *ret = cJSON_AddStringToObject(__xenviron, name, value);
	if (!ret)
		return -1;

	return 0;
}

int putenv(const char *str)
{
	if (!str)
		return -1;

	const char *e = NULL;
	for (const char *z = str; *z; z++)
	{
		if (*z == '=')
		{
			e = z;
			break;
		}
	}

	if (!e)
		return -1;

	char *s = strdup(str);

	if (!s)
		return -1;

	s[e - str] = '\0';

	int ret = __put_env(s, s + (e - str) + 1, 1);
	free(s);

	return ret;
}

char *getenv(const char *name)
{
	cJSON *jvalue = cJSON_GetObjectItemCaseSensitive(__xenviron, name);

	if (jvalue)
	{
		return cJSON_GetStringValue(jvalue);
	}
	return NULL;
}

int setenv(const char *name, const char *val, int overwrite)
{
	const char *z;

	if (!name || !name[0])
		return -1;

	for (z = name; *z; z++)
	{
		if (*z == '=')
			return -1;
	}

	return __put_env(name, val, overwrite);
}

int unsetenv(const char *name)
{
	const char *z;

	if (!name || !name[0])
		return -1;

	for (z = name; *z; z++)
	{
		if (*z == '=')
			return -1;
	}

	cJSON_DeleteItemFromObjectCaseSensitive(__xenviron, name);
	return 0;
}

int clearenv(void)
{
	cJSON_Delete(__xenviron);
	__xenviron = cJSON_CreateObject();
	return 0;
}